package com.lineage.server.model.Instance;

import com.lineage.server.datatables.lock.CharSkillReading;
import com.lineage.server.model.Instance.npcai.CheckPathpc;
import com.lineage.server.model.L1Character;
import com.lineage.server.model.L1Location;
import com.lineage.server.model.L1Object;
import com.lineage.server.model.L1Teleport;
import com.lineage.server.model.map.L1Map;
import com.lineage.server.model.skill.L1SkillUse;
import com.lineage.server.serverpackets.S_MoveCharPacket;
import com.lineage.server.serverpackets.ServerBasePacket;
import com.lineage.server.types.Point;
import com.lineage.server.world.World;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class pcMove extends NpcMoveExecutor {
	private static final Log _log = LogFactory.getLog(pcMove.class);

	private static final int _hce = 20;

	private static Random _random = new Random();

	private int[] _cxy = null;

	private int _error = 0;

	private int _aie = 0;

	private byte result = 0;

	private byte resulttele = 0;

	private Iterator<int[]> _list = null;

	private final L1PcInstance _pc;

	public pcMove(L1PcInstance pc) {

		this._pc = pc;
	}

	public void setDirectionMove(int dir) {
		if (this._pc.hasSkillEffect(4000))
			return;
		if (this._pc.hasSkillEffect(33) 
				|| this._pc.hasSkillEffect(50) 
				|| this._pc.hasSkillEffect(66)
				|| this._pc.hasSkillEffect(87))
			return;
		if (this._pc.hasSkillEffect(1011))
			return;
		if (this._pc.hasSkillEffect(157))
			return;
		if (this._pc.hasSkillEffect(1009))
			return;
		if (this._pc.hasSkillEffect(4017))
			return;
		if (this._pc.hasSkillEffect(192))
			return;
		if (this._pc.hasSkillEffect(87))
			return;
		if (this._pc.hasSkillEffect(208))
			return;
		if (this._pc.hasSkillEffect(1011))
			return;
		if (this._pc.hasSkillEffect(1009))
			return;
		if (this._pc.hasSkillEffect(1010) || this._pc.hasSkillEffect(1011))
			if (CharSkillReading.get().spellCheck(this._pc.getId(), 37)) {
				L1SkillUse l1skilluse = new L1SkillUse();
				l1skilluse.handleCommands(this._pc, 37, this._pc.getId(), this._pc.getX(), this._pc.getY(), 0, 2);
			} else if (this._pc.getInventory().checkItem(40895, 1L)) {
				L1SkillUse l1skilluse = new L1SkillUse();
				l1skilluse.handleCommands(this._pc, 37, this._pc.getId(), this._pc.getX(), this._pc.getY(), 0, 2);
				this._pc.getInventory().consumeItem(40895, 1L);
			}
		if (dir >= 0) {
			int locx = this._pc.getX();
			int locy = this._pc.getY();
			locx += NpcMoveExecutor.HEADING_TABLE_X[dir];
			locy += NpcMoveExecutor.HEADING_TABLE_Y[dir];
			this._pc.setHeading(dir);
			this._pc.getMap().setPassable(this._pc.getLocation(), true);
			this._pc.setX(locx);
			this._pc.setY(locy);
			this._pc.getMap().setPassable(this._pc.getLocation(), false);
			this._pc.broadcastPacketAll(new S_MoveCharPacket(this._pc));
			this._pc.sendPackets( new S_MoveCharPacket(this._pc));
			if (this.result > 8) {
				L1Teleport.teleport(this._pc, this._pc.getLocation(), dir, false);
				this.result = 0;
			}
			result++;
		}
	}

	public static void randomTeleport(L1PcInstance pc, boolean effectable) {
		try {
			L1Location newLocation = pc.getLocation().randomLocation(200, true);
			int newX = newLocation.getX();
			int newY = newLocation.getY();
			short mapId = (short) newLocation.getMapId();
			L1Teleport.teleport(pc, newX, newY, mapId, 5, effectable);
		} catch (Exception e) {
			_log.error(e.getLocalizedMessage(), e);
		}
	}

	public void clear() {
		if (this._list != null)
			this._list = null;
		this._aie = 0;
		this._error = 0;
		this._cxy = null;
	}

	public int moveDirection(int x, int y) {
		int dir = 0;
		try {
			double d = this._pc.getLocation().getLineDistance(new Point(x, y));
			if (d > 20.0D)
				return -1;
			if (d > 20.0D) {
				dir = targetReverseDirection(x, y);
				dir = _targetDirection(this._pc.getHeading(), this._pc.getX(), this._pc.getY(), x, y);
				dir = checkObject(dir);
			} else {
				dir = _serchCource(x, y);
				if (glanceCheck(x, y, dir))
					clear();
				if (dir == -1) {
					this._error++;
					if (this._error != 10) {
						dir = targetReverseDirection(x, y);
						dir = _targetDirection(this._pc.getHeading(), this._pc.getX(), this._pc.getY(), x, y);
						if (!_exsistCharacterBetweenTarget(dir)) {
							dir = checkObject(dir);
							if (dir == -1)
								dir = _check_targe_re8(x, y, d);
						}
					}
				}
			}
		} catch (Exception e) {
			_log.error(e.getLocalizedMessage(), e);
		}
		return dir;
	}

	private int _check_targe_re8(int x, int y, double d) {
		int dir = _check_targe(x - 1, y, d);
		if (dir != -1)
			return dir;
		dir = _check_targe(x - 1, y - 1, d);
		if (dir != -1)
			return dir;
		dir = _check_targe(x + 1, y, d);
		if (dir != -1)
			return dir;
		dir = _check_targe(x + 1, y + 1, d);
		if (dir != -1)
			return dir;
		dir = _check_targe(x + 1, y - 1, d);
		if (dir != -1)
			return dir;
		dir = _check_targe(x - 1, y + 1, d);
		if (dir != -1)
			return dir;
		dir = _check_targe(x, y - 1, d);
		if (dir != -1)
			return dir;
		dir = _check_targe(x, y + 1, d);
		if (dir != -1)
			return dir;
		return -1;
	}

	private int _check_targe(int x, int y, double d) {
		try {
			if (this._pc.is_now_target() == null) {
				clear();
				return -1;
			}
			if (!this._pc.getMap().isPassableDna(x, y, 0))
				return -1;
			if (this._aie <= 20) {
				if (d > 20.0D)
					return -1;
				this._list = null;
				CheckPathpc maze = new CheckPathpc(x, y, 20, this._pc);
				this._cxy = maze.cxy();
				this._list = (Iterator) maze.findBestPath().iterator();
				if (!this._list.hasNext()) {
					this._aie++;
					this._list = null;
					this._cxy = null;
					return -1;
				}
				int[] xy = this._list.next();
				this._list.remove();
				int cx = this._cxy[0] + xy[0];
				int cy = this._cxy[1] + xy[1];
				return _targetDirection(this._pc.getHeading(), this._pc.getX(), this._pc.getY(), cx, cy);
			}
		} catch (Exception e) {
			clear();
		}
		return -1;
	}

	private boolean _exsistCharacterBetweenTarget(int dir) {
		try {
			if (this._pc.is_now_target() == null)
				return false;
			int locX = this._pc.getX();
			int locY = this._pc.getY();
			int targetX = locX + NpcMoveExecutor.HEADING_TABLE_X[dir];
			int targetY = locY + NpcMoveExecutor.HEADING_TABLE_Y[dir];
			ArrayList<L1Object> objects = World.get().getVisibleObjects((L1Object) this._pc, 1);
			Iterator<L1Object> iter = objects.iterator();
			while (iter.hasNext()) {
				L1Object object = iter.next();
				boolean isCheck = false;
				if (object.getX() == targetX && object.getY() == targetY && object.getMapId() == this._pc.getMapId())
					isCheck = true;
				if (isCheck) {
					boolean isHate = false;
					if (object instanceof L1MonsterInstance)
						isHate = true;
					if (isHate) {
						L1Character cha = (L1Character) object;
						this._pc._hateList.add(cha, 0);
						this._pc.setNowTarget(cha);
						return true;
					}
				}
			}
		} catch (Exception e) {
			_log.error(e.getLocalizedMessage(), e);
		}
		return false;
	}

	public int targetReverseDirection(int tx, int ty) {
		int dir = _targetDirection(this._pc.getHeading(), this._pc.getX(), this._pc.getY(), tx, ty);
		return NpcMoveExecutor.HEADING_RD[dir];
	}

	private static int _targetDirection(int h, int x, int y, int tx, int ty) {
		try {
			float dis_x = Math.abs(x - tx);
			float dis_y = Math.abs(y - ty);
			float dis = Math.max(dis_x, dis_y);
			if (dis == 0.0F)
				return h;
			int avg_x = (int) Math.floor((dis_x / dis + 0.59F));
			int avg_y = (int) Math.floor((dis_y / dis + 0.59F));
			int dir_x = 0;
			int dir_y = 0;
			if (x < tx)
				dir_x = 1;
			if (x > tx)
				dir_x = -1;
			if (y < ty)
				dir_y = 1;
			if (y > ty)
				dir_y = -1;
			if (avg_x == 0)
				dir_x = 0;
			if (avg_y == 0)
				dir_y = 0;
			switch (dir_x) {
			case -1:
				switch (dir_y) {
				case -1:
					return 7;
				case 0:
					return 6;
				case 1:
					return 5;
				}
				break;
			case 0:
				switch (dir_y) {
				case -1:
					return 0;
				case 1:
					return 4;
				}
				break;
			case 1:
				switch (dir_y) {
				case -1:
					return 1;
				case 0:
					return 2;
				case 1:
					return 3;
				}
				break;
			}
		} catch (Exception e) {
			_log.error(e.getLocalizedMessage(), e);
		}
		return h;
	}

	private boolean glanceCheck(int tx, int ty, int th) {
		L1Map map = this._pc.getMap();
		int chx = this._pc.getX();
		int chy = this._pc.getY();
		int i = 0;
		while (i < 15 && (chx != tx || chy != ty) && (chx + 1 != tx || chy - 1 != ty) && (chx + 1 != tx || chy != ty)
				&& (chx + 1 != tx || chy + 1 != ty) && (chx != tx || chy + 1 != ty) && (chx - 1 != tx || chy + 1 != ty)
				&& (chx - 1 != tx || chy != ty) && (chx - 1 != tx || chy - 1 != ty) && (chx != tx || chy - 1 != ty)) {
			if (!map.isPassable(chx, chy, th, this._pc))
				return false;
			if (map.isExistDoor(chx, chy) == 3)
				return false;
			if (chx < tx) {
				if (chy == ty) {
					chx++;
				} else if (chy > ty) {
					chx++;
					chy--;
				} else if (chy < ty) {
					chx++;
					chy++;
				}
			} else if (chx == tx) {
				if (chy < ty) {
					chy++;
				} else if (chy > ty) {
					chy--;
				}
			} else if (chx > tx) {
				if (chy == ty) {
					chx--;
				} else if (chy < ty) {
					chx--;
					chy++;
				} else if (chy > ty) {
					chx--;
					chy--;
				}
			}
			i++;
		}
		return true;
	}

	public int checkObject(int h) {
		if (h >= 0 && h <= 7) {
			int x = this._pc.getX();
			int y = this._pc.getY();
			int h2 = NpcMoveExecutor._heading2[h];
			int h3 = NpcMoveExecutor._heading3[h];
			if (this._pc.getMap().isPassableDna(x, y, h))
				return h;
			if (this._pc.getMap().isPassableDna(x, y, h2))
				return h2;
			if (this._pc.getMap().isPassableDna(x, y, h3))
				return h3;
		}
		return -1;
	}

	public int openDoor(int h) {
		return h;
	}

	private void _getFront(int[] ary, int d) {
		switch (d) {
		case 1:
			ary[4] = 2;
			ary[3] = 0;
			ary[2] = 1;
			ary[1] = 3;
			ary[0] = 7;
			break;
		case 2:
			ary[4] = 2;
			ary[3] = 4;
			ary[2] = 0;
			ary[1] = 1;
			ary[0] = 3;
			break;
		case 3:
			ary[4] = 2;
			ary[3] = 4;
			ary[2] = 1;
			ary[1] = 3;
			ary[0] = 5;
			break;
		case 4:
			ary[4] = 2;
			ary[3] = 4;
			ary[2] = 6;
			ary[1] = 3;
			ary[0] = 5;
			break;
		case 5:
			ary[4] = 4;
			ary[3] = 6;
			ary[2] = 3;
			ary[1] = 5;
			ary[0] = 7;
			break;
		case 6:
			ary[4] = 4;
			ary[3] = 6;
			ary[2] = 0;
			ary[1] = 5;
			ary[0] = 7;
			break;
		case 7:
			ary[4] = 6;
			ary[3] = 0;
			ary[2] = 1;
			ary[1] = 5;
			ary[0] = 7;
			break;
		case 0:
			ary[4] = 2;
			ary[3] = 6;
			ary[2] = 0;
			ary[1] = 1;
			ary[0] = 7;
			break;
		}
	}

	private void _moveLocation(int[] ary, int d) {
		switch (d) {
		case 1:
			ary[0] = ary[0] + 1;
			ary[1] = ary[1] - 1;
			break;
		case 2:
			ary[0] = ary[0] + 1;
			break;
		case 3:
			ary[0] = ary[0] + 1;
			ary[1] = ary[1] + 1;
			break;
		case 4:
			ary[1] = ary[1] + 1;
			break;
		case 5:
			ary[0] = ary[0] - 1;
			ary[1] = ary[1] + 1;
			break;
		case 6:
			ary[0] = ary[0] - 1;
			break;
		case 7:
			ary[0] = ary[0] - 1;
			ary[1] = ary[1] - 1;
			break;
		case 0:
			ary[1] = ary[1] - 1;
			break;
		}
		ary[2] = d;
	}

	private int _serchCource(int x, int y) {
		int i;
		int locCenter = L1NpcInstance.DISTANCE + 1;
		int diff_x = x - locCenter;
		int diff_y = y - locCenter;
		int[] locBace = { this._pc.getX() - diff_x, this._pc.getY() - diff_y, 0, 0 };
		int[] locNext = new int[4];
		int[] locCopy;
		int[] dirFront = new int[5];
		boolean[][] serchMap = new boolean[43][43];
		LinkedList<int[]> queueSerch = new LinkedList<int[]>();
		
		
        for (int j = L1NpcInstance.DISTANCE * 2 + 1; j > 0; j--) {
            for (i = L1NpcInstance.DISTANCE - Math.abs(locCenter - j); i >= 0; i--) {
                serchMap[j][locCenter + i] = true;
                serchMap[j][locCenter - i] = true;
            }
        }

		final int[] firstCource = { 0, 2, 4, 6, 1, 3, 5, 7 };
		i = 0;
		while (i < 8) {
			System.arraycopy(locBace, 0, locNext, 0, 4);
			_moveLocation(locNext, firstCource[i]);
			if (locNext[0] - locCenter == 0 && locNext[1] - locCenter == 0)
				return firstCource[i];
			if (serchMap[locNext[0]][locNext[1]]) {
				int tmpX = locNext[0] + diff_x;
				int tmpY = locNext[1] + diff_y;
				boolean found = false;
				switch (i) {
				case 0:
					found = this._pc.getMap().isPassable(tmpX, tmpY + 1, i);
					break;
				case 1:
					found = this._pc.getMap().isPassable(tmpX - 1, tmpY + 1, i);
					break;
				case 2:
					found = this._pc.getMap().isPassable(tmpX - 1, tmpY, i);
					break;
				case 3:
					found = this._pc.getMap().isPassable(tmpX - 1, tmpY - 1, i);
					break;
				case 4:
					found = this._pc.getMap().isPassable(tmpX, tmpY - 1, i);
					break;
				case 5:
					found = this._pc.getMap().isPassable(tmpX + 1, tmpY - 1, i);
					break;
				case 6:
					found = this._pc.getMap().isPassable(tmpX + 1, tmpY, i);
					break;
				case 7:
					found = this._pc.getMap().isPassable(tmpX + 1, tmpY + 1, i);
					break;
				}
				if (found) {
					locCopy = new int[4];
					System.arraycopy(locNext, 0, locCopy, 0, 4);
					locCopy[2] = firstCource[i];
					locCopy[3] = firstCource[i];
					queueSerch.add(locCopy);
				}
				serchMap[locNext[0]][locNext[1]] = false;
			}
			i++;
		}
		locBace = null;
		while (queueSerch.size() > 0) {
			locBace = queueSerch.removeFirst();
			_getFront(dirFront, locBace[2]);
			i = 4;
			while (i >= 0) {
				System.arraycopy(locBace, 0, locNext, 0, 4);
				_moveLocation(locNext, dirFront[i]);
				if (locNext[0] - locCenter == 0 && locNext[1] - locCenter == 0)
					return locNext[3];
				if (serchMap[locNext[0]][locNext[1]]) {
					int tmpX = locNext[0] + diff_x;
					int tmpY = locNext[1] + diff_y;
					boolean found = false;
					switch (i) {
					case 0:
						found = this._pc.getMap().isPassable(tmpX, tmpY + 1, i);
						break;
					case 1:
						found = this._pc.getMap().isPassable(tmpX - 1, tmpY + 1, i);
						break;
					case 2:
						found = this._pc.getMap().isPassable(tmpX - 1, tmpY, i);
						break;
					case 3:
						found = this._pc.getMap().isPassable(tmpX - 1, tmpY - 1, i);
						break;
					case 4:
						found = this._pc.getMap().isPassable(tmpX, tmpY - 1, i);
						break;
					}
					if (found) {
						 locCopy = new int[4];
						System.arraycopy(locNext, 0, locCopy, 0, 4);
						locCopy[2] = dirFront[i];
						queueSerch.add(locCopy);
					}
					serchMap[locNext[0]][locNext[1]] = false;
				}
				i--;
			}
			locBace = null;
		}
		return -1;
	}
}
